{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "39f10553-d93f-496b-a5c1-7e224fbb7f0f",
   "metadata": {},
   "source": [
    "# 2.4 自动化评测答疑机器人的表现\n",
    "\n",
    "## 🚄 前言\n",
    "\n",
    "新人答疑机器人在实际使用中可能会有一些问题。例如，当新人提问“如何请假”时，机器人可能给出通用的回答，而不是基于制度文件内容进行回答。\n",
    "\n",
    "和常规的软件开发需要测试一样，你也应该在你的答疑机器人项目中建立一套评测体系，确保在类似的问题都能快速定位原因，并且在每次针对一个问题优化后，能对一批问题进行测试，确保此次优化的对答疑机器人的整体效果是正向的。\n",
    "\n",
    "\n",
    "## 🍁 课程目标\n",
    "学完本节课程后，你将能够：\n",
    "\n",
    "* 如何自动化大模型应用评测。\n",
    "* 如何通过 Ragas 对 RAG 应用进行评测。\n",
    "* 如何通过 Ragas 分数来定位并解决问题。\n",
    "\n",
    "<!-- ## 📖 课程目录\n",
    "在本章节课程中，我们首先会通过一个具体的问题来了解当前 RAG 应用的一些问题，然后尝试通过自己实现一个简单的自动化测试，来发现该问题。最后我们会学习如何使用比较成熟的 RAG 应用测试框架 Ragas 来评估 RAG 应用的效果\n",
    "\n",
    "- 1.&nbsp;评估 RAG 应用表现\n",
    "    - 1.1 答疑机器人存在的问题\n",
    "    - 1.2 查看 RAG 检索结果排查问题\n",
    "    - 1.3 尝试建立自动化测试机制 \n",
    "  \n",
    "- 2.&nbsp;使用 Ragas 来评估应用表现\n",
    "     - 2.1 评估 RAG 应用回答质量\n",
    "       - 2.1.1 快速上手\n",
    "       - 2.1.2 了解 answer correctness 计算过程\n",
    "     - 2.2 评估检索召回效果\n",
    "       - 2.2.1 快速上手\n",
    "       - 2.2.2 了解 context recall 和 context precision 计算过程 -->"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c917f6bc",
   "metadata": {},
   "source": [
    "## 1. 评估 RAG 应用表现\n",
    "\n",
    "### 1.1 答疑机器人存在的问题\n",
    "\n",
    "在前面的章节中，你完成了一个答疑机器人开发，但你发现它目前在员工查询类的问题上表现不佳。\n",
    "\n",
    "比如，张伟是员工信息表里的第一个员工，但是在你的答疑机器人中却无法回答「张伟是哪个部门的」。\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i1/O1CN01MsuZGI1E0rrkVNNnO_!!6000000000290-0-tps-1626-278.jpg\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i1/O1CN01MsuZGI1E0rrkVNNnO_!!6000000000290-0-tps-1626-278.jpg\" width=\"600\">\n",
    "</a>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33d09a89",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from config.load_key import load_key\n",
    "load_key()\n",
    "print(f'''你配置的 API Key 是：{os.environ[\"DASHSCOPE_API_KEY\"][:5]+\"*\"*5}''')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "947e89be",
   "metadata": {},
   "outputs": [],
   "source": [
    "from chatbot import rag\n",
    "query_engine = rag.create_query_engine(rag.load_index())\n",
    "print('提问：张伟是哪个部门的')\n",
    "response = query_engine.query('张伟是哪个部门的')\n",
    "print('回答：', end='')\n",
    "response.print_response_stream()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "793fa40e",
   "metadata": {},
   "source": [
    "### 1.2. 查看 RAG 检索结果排查问题\n",
    "为了解决这个问题，你需要确认在你的答疑机器人回答问题前，召回的参考资料里是否有张伟的相关资料。\n",
    "\n",
    "通过下面的代码，可以获取到答疑机器人回答这次问题时检索到的参考信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79691f6b",
   "metadata": {},
   "outputs": [],
   "source": [
    "contexts = [node.get_content() for node in response.source_nodes]\n",
    "contexts"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48bbcc22",
   "metadata": {},
   "source": [
    "由此可见，这一问题是检索效果不佳造成的。\n",
    "\n",
    "> 这一章节将专注于建立自动化测试，本身这一检索效果问题将会在后续章节学习中解决。\n",
    "\n",
    "### 1.3. 尝试建立自动化测试机制\n",
    "\n",
    "尽管你总是能想办法定位到问题，但是如果每次都要这样去确认是检索出错、还是检索正确但模型生成答案出错，会非常耗时。你应该建立一个测试机制，能够自动地对你准备的一批问题进行测试。\n",
    "\n",
    "\n",
    "在前面的学习中，你已经知道大模型可以用来回答问题、检查错误。同样的，大模型也可以用于检测答疑机器人的回复是否准确回答了问题，只要在提示词中提供参考信息并限制回答样式即可。\n",
    "\n",
    "下边的`test_answer` 函数可以用来检测答疑机器人的回答是否有效回答了问题，你需要在提示词中传入问题与答疑机器人的回答，并限制回答样式为：“只能是：有效回答 或者 无效回答”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9a20f5b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from chatbot import llm\n",
    "\n",
    "def test_answer(question, answer):\n",
    "    prompt = (\"你是一个测试人员。\\n\"\n",
    "        \"你需要检测下面的这段回答是否有效回答了用户的问题。\\n\"\n",
    "        \"回复只能是：有效回答 或者 无效回答。请勿给出其他信息。\\n\"\n",
    "        \"------\"\n",
    "        f\"回答是 {answer}\"\n",
    "        \"------\"\n",
    "        f\"问题是： {question}\"\n",
    "    )\n",
    "    return llm.invoke(prompt,model_name=\"qwen-max\")\n",
    "\n",
    "test_answer(\"张伟是哪个部门的\", \"根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7c22a73",
   "metadata": {},
   "source": [
    "传入的回答没有有效地回复“张伟是哪个部门的”这个问题，大模型回复的“无效回答”符合预期。\n",
    "\n",
    "在RAG应用中，除了回答的有效性，你还需要确保检索到的参考信息是否有用。下边的`test_contexts` 函数可以用来检测检索到的参考信息是否有效，你需要在提示词中传入问题与检索到的参考信息，并限制回答样式为：“只能是：参考信息有用 或者 参考信息无用”。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e20f11a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_contexts(question, contexts):\n",
    "    prompt = (\"你是一个测试人员。\\n\"\n",
    "        \"你需要检测下面的这些参考资料是否能对回答问题有帮助。\\n\"\n",
    "        \"回复只能是：参考信息有用 或者 参考信息无用。请勿给出其他信息。\\n\"\n",
    "        \"------\"\n",
    "        f\"参考资料是 {contexts}\"\n",
    "        \"------\"\n",
    "        f\"问题是： {question}\"\n",
    "    )\n",
    "    return llm.invoke(prompt,model_name=\"qwen-max\")\n",
    "test_contexts(\"张伟是哪个部门的\", \"核，提供⾏政管理与协调⽀持，优化⾏政⼯作流程。 \\n⾏政部 秦⻜ 蔡静 G705 034 ⾏政 ⾏政专员 13800000034 qinf@educompany.com 维护公司档案与信息系统，负责公司通知及公告的发布，\\n\\n⽀持。 \\n绩效管理部 韩杉 李⻜ I902 041 ⼈⼒资源 绩效专员 13800000041 hanshan@educompany.com 建⽴并维护员⼯绩效档案，定期组织绩效评价会议，协调各部⻔反馈，制定考核流程与标准，确保绩效\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24e550e2",
   "metadata": {},
   "source": [
    "有了上面的两个方法，你已经初步搭建好了一个大模型测试工程的雏形。但截至目前的实现还并不完善，比如：\n",
    "\n",
    "- 因为大模型有时候会捏造事实（幻觉），其给出的答案看起来就像是真的一样，对于这种情况 `test_answer` 方法并不能很好的检测出来。\n",
    "- 检索召回的参考信息中相关的信息占比越多越好（信噪比），但目前我们的测试方法还比较简单，没有考虑这些。\n",
    "\n",
    "你可以考虑使用一些成熟的测试框架来进一步完善你的测试工程，比如 [Ragas](https://docs.ragas.io/en/stable)，它是一个专门设计用于评估 RAG 应用表现的测试框架。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69b87635",
   "metadata": {},
   "source": [
    "\n",
    "## 2. RAG 自动化评测体系\n",
    "为了系统化地评测RAG系统，业界出现了一些非常实用的开源自动化评测框架，这些框架通常会从以下几个维度进行评估：\n",
    "- **召回质量 (Retrieval Quality)**： RAG系统是否检索到了正确且相关的文档片段？\n",
    "- **答案忠实度 (Faithfulness)**： 生成的答案是否完全基于检索到的上下文，没有“胡编乱造”（幻觉）？\n",
    "- **答案相关性 (Answer Relevance)**： 生成的答案是否准确地回答了用户的问题？\n",
    "- **上下文利用率/效率 (Context Utilization/Efficiency)**： 大模型是否有效地利用了所有提供给它的上下文信息？（这与我们之前提到的“Lost in the Middle”和“知识浓度”密切相关）\n",
    "这里介绍几个当前比较流行且功能强大的RAG评测框架：\n",
    "\n",
    "### 2.1 Ragas\n",
    "这是领域里非常出名的一个框架，它的核心思想是“让大模型来帮助你评估RAG系统”。在评测时，Ragas 会调用一个大模型作为评测专家来阅读你的问题、RAG检索到的上下文和生成的答案，然后根据预设的指标给出分数。Ragas的评估指标高度契合RAG系统的痛点，主要包括：\n",
    "- 整体回答质量的评估：\n",
    "    - Answer Correctness，用于评估 RAG 应用生成答案的准确度。\n",
    "- 生成环节的评估：\n",
    "    - Answer Relevancy，用于评估 RAG 应用生成的答案是否与问题相关。\n",
    "    - Faithfulness，用于评估 RAG 应用生成的答案和检索到的参考资料的事实一致性。\n",
    "- 召回阶段的评估：\n",
    "    - Context Precision，用于评估 contexts 中与准确答案相关的条目是否排名靠前、占比高（信噪比）。\n",
    "    - Context Recall，用于评估有多少相关参考资料被检索到，越高的得分意味着更少的相关参考资料被遗漏。\n",
    "\n",
    "该框架提供了Python库，只需几行代码就能集成到你的RAG工作流中，也是本章后续介绍的重点。\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i4/O1CN01b2lVQp21JZCJy6Nfe_!!6000000006964-0-tps-739-420.jpg\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i4/O1CN01b2lVQp21JZCJy6Nfe_!!6000000006964-0-tps-739-420.jpg\" width=\"500\">\n",
    "</a>\n",
    "\n",
    "\n",
    "### 2.2 TruLens\n",
    "这个框架专注于评估的可观测性，它不仅仅告诉你RAG哪里可能有问题，它还能帮你追溯RAG的整个运行过程。包括每次运行的输入、输出、中间步骤、调用的大模型、检索召回的上下文等等，并提供一系列“反馈函数”来自动化评估这些运行。\n",
    "TruLens 框架可以与LangChain、LlamaIndex等框架无缝集成，并提供了一套可视化工具，展示每次RAG调用的详细过程。同时，TruLens 提供了细粒度的反馈函数，从多个角度评估RAG性能，帮助你快速定位问题。其主要评估指标包括：\n",
    "- Groundedness：生成的答案是否是来自于检索召回的知识\n",
    "- Answer Relevance：生成的答案是否与问题相关\n",
    "- Context Relevance：评估召回的知识是否跟问题相关。\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i3/O1CN01OFrK5k1rqruE8ItZR_!!6000000005683-2-tps-1921-1099.png\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i3/O1CN01OFrK5k1rqruE8ItZR_!!6000000005683-2-tps-1921-1099.png\" width=\"500\">\n",
    "</a>\n",
    "\n",
    "你也可以访问 [TruLens官网](https://www.trulens.org)了解其他评估指标如（User sentiment、Fairness and bias）等等。\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i2/O1CN01oQQBuo1gaPuCWG0S8_!!6000000004158-2-tps-3450-1584.png\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i2/O1CN01oQQBuo1gaPuCWG0S8_!!6000000004158-2-tps-3450-1584.png\" width=\"500\">\n",
    "</a>\n",
    "\n",
    "\n",
    "### 2.3 DeepEval\n",
    "DeepEval 则将传统软件开发的测试理念引入到RAG应用中。它鼓励你采用单元测试和测试驱动开发（TDD）的思想，在RAG功能开发前就编写评估测试，确保应用性能从一开始就符合预期。\n",
    "为此，DeepEval提供了一个专门的测试框架，让你能够像编写传统软件的单元测试一样，为RAG系统创建LLM评估测试。它支持为每个测试用例设置明确的通过/失败（Pass/Fail）阈值，这使得DeepEval能轻松集成到你的持续集成/持续部署（CI/CD）流程中，从而实现自动化回归测试。\n",
    "DeepEval 的主要评测指标类似Ragas，也支持Faithfulness, Answer Relevance, Context Relevance, Context Recall等。\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i3/O1CN016YwgTU1P37e5tdyEf_!!6000000001784-2-tps-3272-2508.png\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i3/O1CN016YwgTU1P37e5tdyEf_!!6000000001784-2-tps-3272-2508.png\" width=\"500\">\n",
    "</a>\n",
    "\n",
    "你可以访问[DeepEval官网](https://deepeval.com/docs/getting-started)了解更多内容。\n",
    "\n",
    "### 2.4 自定义评测框架\n",
    "除了前面介绍的Ragas、TruLens、DeepEval等专业评测框架，你可能也接触过其他的开源评测工具，比如**LlamaIndex**和**LangChain**这些主流RAG开发框架本身也有**内置评估工具**。这些评估工具能让你在构建RAG流程的同时，方便地进行性能评估，实现无缝衔接。但是，在某些特定场景下，你可能需要更灵活、更贴合业务的评估方式，这时你也可以选择**自定义评测框架**。\n",
    "\n",
    "对于评测一个智能客服系统而言，你不仅仅想知道RAG有没有“瞎说”（Faithfulness）或者答案是否“相关”（Relevance），你更想知道它是否符合你业务的特定规则，或者提供的答案是否真正**解决了用户的痛点**。\n",
    "\n",
    "- 案例一：当用户询问“**报销的流程是什么？**”，你们的业务专家可能会要求“好的报销答案，必须明确包含报销单链接和二级主管审批信息。”或者“好的答案，必须引导用户到费用系统提交报销申请。”，这样才能满足“**流程合规**”的要求。\n",
    "- 案例二：当用户询问“**订单延迟了怎么办？**”，更好的回答可能是先要“**安抚用户的情绪，再给出物流状态，预期送达时间、延误原因（如果已知）、以及后续处理（如退款/投诉）的指引。**”而不仅仅是回答“**您的订单正在派送中，请耐心等待**”。\n",
    "  \n",
    "因此，你可以把“**流程可操作性**”、“**政策合规性**”、“**情绪安抚度**”、“**问题解决完备性**”等指标，加入你的自定义评测框架。\n",
    "\n",
    "> 请注意：**领域专家的深度参与是评测系统乃至AI应用成功的关键**。使用自动化评测框架并非要让机器彻底取代人工判断，而是旨在**为评测提效**。前面介绍的许多自动化框架（如Ragas）会利用大模型来充当“评委”，对RAG表现进行初步判断。然而，只有**领域专家**才能提供那些最宝贵的、真正反映业务需求的**高价值**“**正确答案**”（**Ground Truth**），并对用户提问和RAG回答的正确性进行权威性的审定。\n",
    "\n",
    "因此，我们必须让领域专家深度参与到**评测集的构建**和**系统性评估**中。自动化评测的真正价值，在于它能将专家提供的这些宝贵Ground Truth或评估标准，转化为大规模、可重复的日常评测任务，从而**解放专家的时间**，让他们聚焦于更具挑战性的问题分析和优化策略制定，而非陷入重复性的判断劳动中。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d3a0e7e9",
   "metadata": {},
   "source": [
    "## 3. 使用 Ragas 来评估应用表现\n",
    "\n",
    "你可以使用Ragas来做全链路评测，这只需要在你的Python项目中加入几行代码。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d819011",
   "metadata": {},
   "source": [
    "### 3.1 评估 RAG 应用回答质量\n",
    "\n",
    "#### 3.1.1 快速上手"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86554952",
   "metadata": {},
   "source": [
    "在评估 RAG 应用整体回答质量时，使用 Ragas 的 Answer Correctness 是一个很好的指标。为了计算这个指标，你需要准备以下两种数据来评测 RAG 应用产生的 answer 质量：\n",
    "1. question（输入给 RAG 应用的问题）\n",
    "2. ground_truth（你预先知道的正确的答案）\n",
    "\n",
    "为了便于展示不同回答的评测指标差异，我们针对问题「*张伟是哪个部门的*」准备了三组 RAG 应用回答：\n",
    "\n",
    "| question            | ground_truth     | answer                                                                 |\n",
    "|---------------------|------------------|------------------------------------------------------------------------|\n",
    "| 张伟是哪个部门的    | 张伟是教研部的。 | 根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。（无效答案） | \n",
    "| 张伟是哪个部门的    | 张伟是教研部的。 | 张伟是人事部门的。（幻觉）                                                     |\n",
    "| 张伟是哪个部门的    | 张伟是教研部的。 | 张伟是教研部的。（正确）                                                       |\n",
    "\n",
    "然后我们就可以运行下面的代码，来计算回答准确度（即 Answer Correctness）的得分。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "39940304",
   "metadata": {},
   "outputs": [],
   "source": [
    "from tqdm.cli import tqdm as tqdm_cli\n",
    "import tqdm.auto\n",
    "tqdm.auto.tqdm = tqdm_cli\n",
    "\n",
    "from langchain_community.llms.tongyi import Tongyi\n",
    "from langchain_community.embeddings import DashScopeEmbeddings\n",
    "from datasets import Dataset\n",
    "from ragas import evaluate\n",
    "from ragas.metrics import answer_correctness\n",
    "\n",
    "data_samples = {\n",
    "    'question': [\n",
    "        '张伟是哪个部门的？',\n",
    "        '张伟是哪个部门的？',\n",
    "        '张伟是哪个部门的？'\n",
    "    ],\n",
    "    'answer': [\n",
    "        '根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。',\n",
    "        '张伟是人事部门的',\n",
    "        '张伟是教研部的'\n",
    "    ],\n",
    "    'ground_truth':[\n",
    "        '张伟是教研部的成员',\n",
    "        '张伟是教研部的成员',\n",
    "        '张伟是教研部的成员'\n",
    "    ]\n",
    "}\n",
    "\n",
    "dataset = Dataset.from_dict(data_samples)\n",
    "score = evaluate(\n",
    "    dataset = dataset,\n",
    "    metrics=[answer_correctness],\n",
    "    llm=Tongyi(model_name=\"qwen-plus\"),\n",
    "    embeddings=DashScopeEmbeddings(model=\"text-embedding-v3\")\n",
    ")\n",
    "score.to_pandas()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7aa2370",
   "metadata": {},
   "source": [
    "<div>\n",
    "<style scoped>\n",
    "    .dataframe tbody tr th:only-of-type {\n",
    "        vertical-align: middle;\n",
    "    }\n",
    "\n",
    "    .dataframe tbody tr th {\n",
    "        vertical-align: top;\n",
    "    }\n",
    "\n",
    "    .dataframe thead th {\n",
    "        text-align: right;\n",
    "    }\n",
    "</style>\n",
    "<table border=\"1\" class=\"dataframe\">\n",
    "  <thead>\n",
    "    <tr style=\"text-align: right;\">\n",
    "      <th></th>\n",
    "      <th>question</th>\n",
    "      <th>answer</th>\n",
    "      <th>ground_truth</th>\n",
    "      <th>answer_correctness</th>\n",
    "    </tr>\n",
    "  </thead>\n",
    "  <tbody>\n",
    "    <tr>\n",
    "      <th>0</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>0.175227</td>\n",
    "    </tr>\n",
    "    <tr>\n",
    "      <th>1</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>张伟是人事部门的</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>0.193980</td>\n",
    "    </tr>\n",
    "    <tr>\n",
    "      <th>2</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>张伟是教研部的</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>0.994619</td>\n",
    "    </tr>\n",
    "  </tbody>\n",
    "</table>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55141147",
   "metadata": {},
   "source": [
    "可以看到，Ragas 的 Answer Correctness 指标准确的反映出了三种回答的表现，越符合事实的answer得分越高。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6bd01530",
   "metadata": {},
   "source": [
    "#### 3.1.2 了解 answer correctness 的计算过程\n",
    "\n",
    "从直观感受上 Answer correctness 的打分确实与你的预期相符。它在打分过程使用到了大模型（代码中的`llm=Tongyi(model_name=\"qwen-plus\")`）与 embedding 模型（代码中的`embeddings=DashScopeEmbeddings(model=\"text-embedding-v3\")`），由 answer 和 ground_truth 的**语义相似度**和**事实准确度**计算得出。\n",
    "\n",
    "##### 语义相似度 \n",
    "语义相似度是通过 embedding 模型得到 answer 和 ground_truth 的文本向量，然后计算两个文本向量的相似度。向量相似度的计算有许多种方法，如余弦相似度、欧氏距离、曼哈顿距离等， Ragas 使用了最常用的余弦相似度。\n",
    "\n",
    "##### 事实准确度\n",
    "\n",
    "事实准确度是衡量 answer 与 ground_truth 在事实描述上差异的指标。比如以下两个描述：\n",
    "\n",
    "- answer：张伟是教研部负责大模型课程的同事。\n",
    "- ground_truth：张伟是教研部负责大数据方向的同事。\n",
    "\n",
    "answer 和 ground_truth 在事实描述上存在差异（工作方向），但也存在一致的地方（工作部门）。这样的差异很难通过大模型或 embedding 模型的简单调用来量化。Ragas 通过大模型将 answer 与 ground_truth 分别生成各自的观点列表，并对观点列表中的元素进行比较与计算。\n",
    "\n",
    "下图可以帮助你理解 Ragas 衡量事实准确度的方法：\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i3/O1CN01NXmu4B1xpOGyZDDdB_!!6000000006492-0-tps-2382-1186.jpg\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i3/O1CN01NXmu4B1xpOGyZDDdB_!!6000000006492-0-tps-2382-1186.jpg\" width=\"600\">\n",
    "</a>\n",
    "\n",
    "1.  通过大模型将answer、ground_truth分别生成各自的观点列表。比如：  \n",
    "    - **生成 answer 的观点列表**: 张伟是教研部负责大模型课程的同事。 ---> [\"*张伟是教研部的*\", \"*张伟负责大模型课程*\"]  \n",
    "    - **生成 ground_truth 的观点列表**：张伟是教研部负责大数据方向的同事。---> [\"*张伟是教研部的*\", \"*张伟负责大数据方向*\"]\n",
    "    \n",
    "2.  遍历answer与ground_truth列表，并初始化三个列表，TP、FP与FN。 \n",
    "    - 对于由**answer**生成的观点：\n",
    "      - 如果该观点与ground_truth的观点相匹配，则将该观点添加到TP列表中。比如：「*张伟是教研部的*」。\n",
    "      - 如果该观点在 ground_truth 的观点列表中找不到依据，则将该观点添加到FP列表中。比如：「*张伟负责大模型课程*」。\n",
    "    - 对于**ground_truth**生成的观点：\n",
    "      - 如果该观点在 answer 的观点列表中找不到依据，则将该陈述添加到FN列表中。比如：「*张伟负责大数据方向*」。       \n",
    "    <!-- ![image](https://alidocs.oss-cn-zhangjiakou.aliyuncs.com/res/ybEnBBXZ6LoPnP13/img/86c4839b-bd48-4faf-9842-c42599181339.png) -->\n",
    "      > 该步骤的判断过程均由大模型提供。\n",
    "    \n",
    "3.  统计TP、FP与FN列表的元素个数，并按照以下方式计算f1 score分数：    \n",
    "\n",
    "```shell\n",
    "f1 score = tp / (tp + 0.5 * (fp + fn)) if tp > 0 else 0\n",
    "```\n",
    "\n",
    "以上文为例：f1 score = 1/(1+0.5\\*(1+1)) = 0.5\n",
    "\n",
    "##### 分数汇总\n",
    "得到语义相似度和事实准确度的分数后，对两者加权求和，即可得到最终的 Answer Correctness 的分数。\n",
    "```\n",
    "Answer Correctness 的得分 = 0.25 * 语义相似度得分 + 0.75 * 事实准确度得分\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe5fa7e6",
   "metadata": {},
   "source": [
    "### 3.2 评估检索召回效果\n",
    "\n",
    "#### 3.2.1 快速上手\n",
    "\n",
    "Ragas 中的 context precision 和 context recall 指标可以用于评估 RAG 应用中的检索的召回效果。\n",
    "\n",
    "- Context precision 会评估检索召回的参考信息（contexts）中与准确答案相关的条目是否排名靠前、占比高（信噪比），**侧重相关性**。\n",
    "- Context recall 则会评估 contexts 与 ground_truth 的事实一致性程度，**侧重事实准确度**。\n",
    "\n",
    "实际应用时，可以将两者结合使用。\n",
    "\n",
    "为了计算这些指标，你需要准备的数据集应该包括以下信息：\n",
    "\n",
    "- **question**，输入给 RAG 应用的问题。\n",
    "- **contexts**，检索召回的参考信息。\n",
    "- **ground_truth**，你预先知道的正确的答案。\n",
    "\n",
    "你可以继续使用「*张伟是哪个部门的*」这个问题，准备三组数据，运行下面的代码，来同时计算 context precision 和 context recall 的得分。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8fb227cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.llms.tongyi import Tongyi\n",
    "from datasets import Dataset\n",
    "from ragas import evaluate\n",
    "from ragas.metrics import context_recall,context_precision\n",
    "\n",
    "data_samples = {\n",
    "    'question': [\n",
    "        '张伟是哪个部门的？',\n",
    "        '张伟是哪个部门的？',\n",
    "        '张伟是哪个部门的？'\n",
    "    ],\n",
    "    'answer': [\n",
    "        '根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。',\n",
    "        '张伟是人事部门的',\n",
    "        '张伟是教研部的'\n",
    "    ],\n",
    "    'ground_truth':[\n",
    "        '张伟是教研部的成员',\n",
    "        '张伟是教研部的成员',\n",
    "        '张伟是教研部的成员'\n",
    "    ],\n",
    "    'contexts' : [\n",
    "        ['提供⾏政管理与协调⽀持，优化⾏政⼯作流程。 ', '绩效管理部 韩杉 李⻜ I902 041 ⼈⼒资源'],\n",
    "        ['李凯 教研部主任 ', '牛顿发现了万有引力'],\n",
    "        ['牛顿发现了万有引力', '张伟 教研部工程师，他最近在负责课程研发'],\n",
    "    ],\n",
    "}\n",
    "\n",
    "dataset = Dataset.from_dict(data_samples)\n",
    "score = evaluate(\n",
    "    dataset = dataset,\n",
    "    metrics=[context_recall, context_precision],\n",
    "    llm=Tongyi(model_name=\"qwen-plus\"))\n",
    "score.to_pandas()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b678b82",
   "metadata": {},
   "source": [
    "<div>\n",
    "<style scoped>\n",
    "    .dataframe tbody tr th:only-of-type {\n",
    "        vertical-align: middle;\n",
    "    }\n",
    "\n",
    "    .dataframe tbody tr th {\n",
    "        vertical-align: top;\n",
    "    }\n",
    "\n",
    "    .dataframe thead th {\n",
    "        text-align: right;\n",
    "    }\n",
    "</style>\n",
    "<table border=\"1\" class=\"dataframe\">\n",
    "  <thead>\n",
    "    <tr style=\"text-align: right;\">\n",
    "      <th></th>\n",
    "      <th>question</th>\n",
    "      <th>answer</th>\n",
    "      <th>ground_truth</th>\n",
    "      <th>contexts</th>\n",
    "      <th>context_recall</th>\n",
    "      <th>context_precision</th>\n",
    "    </tr>\n",
    "  </thead>\n",
    "  <tbody>\n",
    "    <tr>\n",
    "      <th>0</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>[提供⾏政管理与协调⽀持，优化⾏政⼯作流程。 , 绩效管理部 韩杉 李⻜ I902 041 ...</td>\n",
    "      <td>0.0</td>\n",
    "      <td>0.0</td>\n",
    "    </tr>\n",
    "    <tr>\n",
    "      <th>1</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>张伟是人事部门的</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>[李凯 教研部主任 , 牛顿发现了万有引力]</td>\n",
    "      <td>0.0</td>\n",
    "      <td>0.0</td>\n",
    "    </tr>\n",
    "    <tr>\n",
    "      <th>2</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>张伟是教研部的</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>[牛顿发现了万有引力, 张伟 教研部工程师，他最近在负责课程研发]</td>\n",
    "      <td>1.0</td>\n",
    "      <td>0.5</td>\n",
    "    </tr>\n",
    "  </tbody>\n",
    "</table>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1938bbd",
   "metadata": {},
   "source": [
    "由上面的数据可以看到：\n",
    "- 最后一行数据的回答是准确的\n",
    "- 过程中检索到的参考资料（contexts）中也包含了正确答案的观点，即「张伟是教研部的」。这一情况体现在了 context recall 得分为 1。\n",
    "- 但是 contexts 中并不是每一条都是和问题及答案相关的，比如「牛顿发现了万有引力」。这一情况体现在了 context precision 得分为 0.5。\n",
    "\n",
    "#### 3.2.2 了解 context recall 和 context precision 的计算过程\n",
    "\n",
    "##### Context recall\n",
    "\n",
    "你已经从上文了解到 context recall 是衡量 contexts 与 ground_truth 是否一致的指标。 \n",
    "\n",
    "在Ragas 中，context recall 用来描述 ground_truth 中有多少比例的观点可以得到 contexts 的支持，计算过程如下：\n",
    "\n",
    "1. 由大模型将 ground_truth 分解成 n 个观点（statements）。  \n",
    "\n",
    "   比如，可以由ground_truth「张伟是教研部的成员」生成观点列表 [张伟是教研部的\"]。\n",
    "2. 由大模型判断每个观点能在检索到的参考资料（contexts）中找到依据，或者说 context 是否能支撑 ground_truth 的观点。  \n",
    "\n",
    "   比如，这个观点在第三行数据的 contexts 中能找到依据「*张伟 教研部工程师，他最近在负责课程研发*」。\n",
    "3. 然后 ground_truth 观点列表中，能在 contexts 中找到依据的观点占比，作为 context_recall 分数。  \n",
    "\n",
    "   这里的得分为 1 = 1/1。\n",
    "\n",
    "##### Context precision\n",
    "\n",
    "在Ragas 中，context precision 不仅衡量了 contexts 中有多少比例的 context 与 ground_truth 相关，还衡量了 contexts 中 context 的排名情况。计算过程比较复杂：\n",
    "\n",
    "\n",
    "\n",
    "1. 按顺序读取 contexts 中的 context<sub>i</sub> ，根据 question 与 ground_truth，判断 context<sub>i</sub>  是否相关。相关为 1 分，否则为 0 分。  \n",
    "   \n",
    "   比如上面第三行数据中，context<sub>1</sub>(牛顿发现了万有引力) 是不相关的，context<sub>2</sub> 相关。\n",
    "2. 对于每一个 context，以该 context 及之前 context 的分数之和作为分子，context 所处排位作为分母，计算 precision 分。  \n",
    "\n",
    "   对于上面第三行数据，context<sub>1</sub> precision 分为 0/1 = 0，context<sub>2</sub> precision 分为 1/2 = 0.5。\n",
    "3. 对每一个 context 的 precision 分求和，除以相关的 context 个数，得到 context_precision。  \n",
    "\n",
    "   对于上面第三行数据，context_precision = (0 + 0.5) / 1 = 0.5。\n",
    "\n",
    "> 如果你暂时无法理解上面的计算过程也没有关系，你只需知道该指标衡量了 contexts 中 context 的排名情况。如果你感兴趣，我们鼓励你去阅读 [Ragas 的源码](https://github.com/explodinggradients/ragas/blob/cc31f65d4b7c7cd6bbf686b9073a0dfaacfbcbc5/src/ragas/metrics/_context_precision.py#L250)。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d7cc24c",
   "metadata": {},
   "source": [
    "### 3.3 其他推荐了解的指标\n",
    "\n",
    "Ragas 还提供了很多其他的指标，这里就不一一介绍，你可以访问 Ragas 的文档来查看更多指标的适用场景和工作原理。\n",
    "\n",
    "Ragas 支持的指标可以访问：https://docs.ragas.io/en/stable/concepts/metrics/available_metrics/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44edd4a3",
   "metadata": {},
   "source": [
    "## 4.如何根据 Ragas 指标进行优化\n",
    "做评测的最终目的不是为了拿到分数，而是根据这些分数确定优化的方向。你已经学习到了answer correctness、context recall、context precision三个指标的概念与计算方法，当你观察到某几个指标的分数较低时，应该制定相应的优化措施。\n",
    "\n",
    "\n",
    "### 4.1 上下文是 RAG 的生命线\n",
    "\n",
    "当用户提出一个问题时，大模型通过阅读理解你提供的 **上下文（Context）** 来给出回答。上下文决定了大模型能否给出准确、完整的答案。如果上下文缺失重要知识点，或存在错误、无关的内容，大模型就无法给出正确的结论。正如你在前面看到的问题一样：如果上下文（Context）里缺少了“张伟部门”的内容，大模型自然无法给出正确答案。\n",
    "\n",
    "于是，为了不丢失有效知识，有人提出把全部可用资料“**一股脑全部灌给大模型**”，让大模型来做甄别，结果产生了一个更复杂的问题：即使某个关键线索确实存在于你提供的大量资料中，但如果它被埋藏在海量的无关信息里，大模型也很可能会“视而不见”，这便是 RAG 系统中常说的“**Lost in the Middle**”现象。试想，关于“张伟部门”的有效信息，可能只是一句简单的“**张伟，技术部，工号XXXX**”，如果这句话被夹杂在几百页的员工考勤记录、会议纪要甚至公司食堂菜单里，大模型很可能就会在这些冗余信息中迷失，导致最终无法给出正确的答案。它的“**注意力**”被分散了，关键信息被“**淹没**”了。\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i4/O1CN01Vvkdkj1ocGQeGkNxI_!!6000000005245-2-tps-1836-992.png\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i4/O1CN01Vvkdkj1ocGQeGkNxI_!!6000000005245-2-tps-1836-992.png\" width=\"500\">\n",
    "</a>\n",
    "\n",
    "所以，RAG 精度的瓶颈，往往不在于大模型本身是否足够“聪明”，而在于你提供给它的上下文（Context）的“**知识浓度**”。一个高质量的上下文，理应具有较高的知识浓度，意味着其中的相关信息密度高、噪音少、与问题直接关联。能让大模型准确地专注问题核心，得出正确结论。因此，你提供的上下文质量，直接决定了 RAG 系统的上限。\n",
    "\n",
    "在 Ragas 中 **Context Recall** 和 **Context Precision** 就是来衡量召回的上下文的有效性的。你可以通过分析这两个指标来确认你的RAG系统**召回上下文的质量**有没有提升。\n",
    "\n",
    "\n",
    "### 4.2 context recall\n",
    "\n",
    "context recall指标评测的是RAG应用在**检索**阶段的表现。如果该指标得分较低，你可以尝试从以下方面进行优化：\n",
    "\n",
    "- **检查知识库**\n",
    "\n",
    "    <img src=\"https://wanx.alicdn.com/wanx/1937257750879544/text_to_image_lite_v2/6e4ca1055a0c467992b6b719b443a33e_0.png?x-oss-process=image/watermark,image_aW1nL3dhdGVybWFyazIwMjQxMTEyLnBuZz94LW9zcy1wcm9jZXNzPWltYWdlL3Jlc2l6ZSxtX2ZpeGVkLHdfMzAzLGhfNTI=,t_80,g_se,x_10,y_10/format,webp\" width=\"300\">\n",
    "\n",
    "    知识库是RAG应用的源头，如果知识库的内容不够完备，则会导致召回的参考信息不充分，从而影响context recall。你可以对比知识库的内容与测试样本，观察知识库的内容是否可以支持每一条测试样本（这个过程你也可以借助大模型来完成）。如果你发现某些测试样本缺少相关知识，则需要对知识库进行补充。\n",
    "- **更换embedding模型**\n",
    "\n",
    "    <img src=\"https://img.alicdn.com/imgextra/i1/O1CN01WjZDdd1FlPjxo2B44_!!6000000000527-0-tps-2476-1120.jpg\" width=\"750\">\n",
    "    \n",
    "    如果你的知识库内容已经很完备，则可以考虑更换embedding模型。好的embedding模型可以理解文本的深层次语义，如果两句话深层次相关，那么即使“看上去”不相关，也可以获得较高的相似度分数。比如提问是“负责课程研发的是谁？”，知识库对应文本段是“张伟是教研部的成员”，尽管重合的词汇较少，但优秀的embedding模型仍然可以为这两句话打出较高的相似度分数，从而将“张伟是教研部的成员”这一文本段召回。\n",
    "- **query改写**\n",
    "\n",
    "    <img src=\"https://img.alicdn.com/imgextra/i4/O1CN01Z28xyO1KXjzp6JNaq_!!6000000001174-0-tps-2352-1154.jpg\" width=\"750\">\n",
    "    \n",
    "    作为开发者，对用户的提问方式做过多要求是不现实的，因此你可能会得到这样缺少信息的问题：“教研部”、“请假”、“项目管理”。如果直接将这样的问题输入RAG应用中，大概率无法召回有效的文本段。你可以通过对员工常见问题的梳理来设计一个prompt模板，使用大模型来改写query，提升召回的准确率。\n",
    "\n",
    "\n",
    "### 4.3 context precision\n",
    "与context recall一样，context precision指标评测的也是RAG应用在**检索**阶段的表现，但是更注重相关的文本段是否具有靠前的排名。如果该指标得分较低，你可以尝试context recall中的优化措施，并且可以尝试在检索阶段加入**rerank**（重排序），来提升相关文本段的排名。\n",
    "\n",
    "\n",
    "\n",
    "### 4.4 answer correctness\n",
    "\n",
    "answer correctness指标评测的是RAG系统整体的综合指标。如果该指标得分较低，而前两项分数较高，说明RAG系统在**检索**阶段表现良好，但是**生成**阶段出了问题。你可以尝试前边教程学到的方法，如优化prompt、调整大模型生成的超参数（如temperature）等，你也可以更换性能更加强劲的大模型，甚至对大模型进行微调（后边的教程会介绍）等方法来提升生成答案的准确度。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77e397cd",
   "metadata": {},
   "source": [
    "\n",
    "## 5. 打造卓越的评测运营体系\n",
    "\n",
    "自动化评测虽然能有效提升评测效率，但是更大的挑战来自于**构建高质量的评测样本**以及如何**凝练、积累深厚的领域知识**。因此，你还需要结合自身业务特点，建立一套完善的评测运营体系。你可以从“**找到最懂业务的人，用科学的方法，深度参与评测**”入手：\n",
    "\n",
    "- **让业务专家参与 AI 评测**：评测的最终目的是提升业务效果，因此需要设定合理的业务目标，驱动**业务专家**来参与评测并沉淀评测方法。例如：让电商客服主管评测促销规则回复准确性，让物流专员评测运费时效回复准确性，让售后专家验证退换货流程回复准确性，等等。\n",
    "- **从最终用户视角出发构建评测体系**：首先，基于真实用户对话样本评估答疑机器人是否有效解答了用户问题，从用户视角评判答案的正确性、简洁性与可操作性。其次，综合线上**用户满意度**统计与抽样**答准率**确立核心评测指标。最后，将评测体系细化为具体的算法指标，如意图识别准确率、问题改写准确率、知识召回准确率、知识库覆盖率及模型生成准确率等。\n",
    "- **持续运营评测体系**：你需要将评测答疑机器人的工作融入业务体系。你可以同时利用标注平台构建**专家评测**机制，并开发**自动化评测**工具，实现两者的**协同运作**。更重要的是，你需要在工作流程中建立“**发现问题 -> 改进优化 -> 跟踪效果 -> 迭代指标**”的闭环机制，让评测持续驱动业务系统改进，最终形成良性循环。\n",
    "- **维护覆盖用户意图的高质量评测集**：除了采集真实用户对话作为评测样本，你也可以收集用户反馈的错误案例（bad case），统计客服记录中的高频问题。你还可以通过知识图谱、对知识做语义化结构化处理，来建立知识库，让领域专家们根据知识库来构建标准评测集，或者通过 AI 辅助生成一些样本。你的目标是构建一个能覆盖用户意图的标准评测集。\n",
    "  \n",
    "当你引入业务领域专家帮助你解决了认知问题，同时持续运营评测标准体系确保评测指标的稳定性之后，你就可以考虑如何将这些知识沉淀到你的自动化评测中。通过自动化提升工作效率，你就可以更好地贯彻执行评测标准。例如，根据用户问答及时发现问题，修订和补充用户需要的内容，或者根据业务目标的变化来调整评测指标的计算口径等等。\n",
    "\n",
    "> 最终目标：让这个“答疑机器人”像贴心店员，准确理解您的需求，用您听得懂的话解决问题。\n",
    "\n",
    "\n",
    "\n",
    "<a href=\"https://img.alicdn.com/imgextra/i4/O1CN01ol2qeS1uu1Ts9zLjb_!!6000000006096-2-tps-2856-1724.png\" target=\"_blank\">\n",
    "<img src=\"https://img.alicdn.com/imgextra/i4/O1CN01ol2qeS1uu1Ts9zLjb_!!6000000006096-2-tps-2856-1724.png\" width=\"800\">\n",
    "</a>\n",
    "\n",
    "**评测运营体系架构图**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "448a70bb-47c0-4933-9ed5-acd3072d18cc",
   "metadata": {},
   "source": [
    "## ✅ 本节小结\n",
    "\n",
    "通过本节内容的学习，你已经学会了怎么为RAG应用建立自动化测试了。\n",
    "\n",
    "自动化测试是工程优化的重要手段。借助量化的自动化测试，可以帮你在改进 RAG 应用时，从**感觉**变好了，转变为指标**量化**显示应用表现更好。这不仅可以帮助你更快地评估RAG应用的问答质量、找到优化方向，还能将你所作出的优化结果量化出来。\n",
    "\n",
    "当然，有了自动化测试并不意味着你就完全不需要人工评估了，建议在实际应用时，邀请 RAG 应用对应的领域专家一起构建能反映真实场景问题分布的测试集，并且持续更新测试集。\n",
    "\n",
    "同时，由于大模型并不能总是做到 100% 准确，也建议你在实际使用时，定期抽样评估自动化测试结果的精度，并且尽量不要频繁更换大模型与 embedding 模型。对于 Ragas，你可以通过调整默认评测方法中的提示词（比如补充和你的业务领域相关的参考样例），来改善其表现（详情请参考拓展阅读）。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e7745583",
   "metadata": {},
   "source": [
    "## 拓展阅读\n",
    "\n",
    "### 更换 Ragas 的提示词模板\n",
    "Ragas 的许多评测指标是基于大模型来实现的。与 LlamaIndex 一样，Ragas 的默认提示词模板是英文的，同时允许自定义修改。你可以将 Ragas 各指标的默认提示词翻译成中文，使得评测的结果更符合中文问答场景。\n",
    "\n",
    "我们在ragas_prompt文件夹中提供了中文的提示词模板，你可以参考以下代码将中文提示词适配到 Ragas 的不同指标中。\n",
    "> Ragas 会在提示词中向大模型提供一些示例，来帮助大模型理解应该如何进行判断、生成观点列表等操作，因此你也可以更改示例来适配到你的业务场景。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5077509",
   "metadata": {},
   "source": [
    "| Ragas 自带 prompt 模板 |\n",
    "|------------------------|\n",
    "| <a href=\"https://img.alicdn.com/imgextra/i4/O1CN016BngIW1nRtnidE0hK_!!6000000005087-0-tps-2710-1334.jpg\" target=\"_blank\"><img src=\"https://img.alicdn.com/imgextra/i4/O1CN016BngIW1nRtnidE0hK_!!6000000005087-0-tps-2710-1334.jpg\" width=\"900\"></a> |\n",
    "\n",
    "| 更改 prompt 模板之后   |\n",
    "|------------------------|\n",
    "| <a href=\"https://img.alicdn.com/imgextra/i2/O1CN01m7wDt21fhSKQ7d8CT_!!6000000004038-0-tps-2548-1278.jpg\" target=\"_blank\"><img src=\"https://img.alicdn.com/imgextra/i2/O1CN01m7wDt21fhSKQ7d8CT_!!6000000004038-0-tps-2548-1278.jpg\" width=\"900\"></a> |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8d949c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入中文提示词模板\n",
    "from ragas_prompt.chinese_prompt import ContextRecall,ContextPrecision,AnswerCorrectness\n",
    "\n",
    "# 进行各指标的自定义 prompt 设置\n",
    "context_recall.context_recall_prompt.instruction = ContextRecall.context_recall_prompt[\"instruction\"]\n",
    "context_recall.context_recall_prompt.output_format_instruction = ContextRecall.context_recall_prompt[\"output_format_instruction\"]\n",
    "context_recall.context_recall_prompt.examples = ContextRecall.context_recall_prompt[\"examples\"]\n",
    "\n",
    "context_precision.context_precision_prompt.instruction = ContextPrecision.context_precision_prompt[\"instruction\"]\n",
    "context_precision.context_precision_prompt.output_format_instruction = ContextPrecision.context_precision_prompt[\"output_format_instruction\"]\n",
    "context_precision.context_precision_prompt.examples = ContextPrecision.context_precision_prompt[\"examples\"]\n",
    "\n",
    "answer_correctness.correctness_prompt.instruction = AnswerCorrectness.correctness_prompt[\"instruction\"]\n",
    "answer_correctness.correctness_prompt.output_format_instruction = AnswerCorrectness.correctness_prompt[\"output_format_instruction\"]\n",
    "answer_correctness.correctness_prompt.examples = AnswerCorrectness.correctness_prompt[\"examples\"]\n",
    "\n",
    "data_samples = {\n",
    "    'question': [\n",
    "        '张伟是哪个部门的？',\n",
    "        '张伟是哪个部门的？',\n",
    "        '张伟是哪个部门的？'\n",
    "    ],\n",
    "    'answer': [\n",
    "        '根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。',\n",
    "        '张伟是人事部门的',\n",
    "        '张伟是教研部的'\n",
    "    ],\n",
    "    'ground_truth':[\n",
    "        '张伟是教研部的成员',\n",
    "        '张伟是教研部的成员',\n",
    "        '张伟是教研部的成员'\n",
    "    ],\n",
    "    'contexts' : [\n",
    "        ['提供⾏政管理与协调⽀持，优化⾏政⼯作流程。 ', '绩效管理部 韩杉 李⻜ I902 041 ⼈⼒资源'],\n",
    "        ['李凯 教研部主任 ', '牛顿发现了万有引力'],\n",
    "        ['牛顿发现了万有引力', '张伟 教研部工程师，他最近在负责课程研发'],\n",
    "    ],\n",
    "}\n",
    "\n",
    "dataset = Dataset.from_dict(data_samples)\n",
    "\n",
    "score = evaluate(\n",
    "    dataset = dataset,\n",
    "    metrics=[answer_correctness,context_recall,context_precision],\n",
    "    llm=Tongyi(model_name=\"qwen-plus\"),\n",
    "    embeddings=DashScopeEmbeddings(model=\"text-embedding-v3\"))\n",
    "\n",
    "score.to_pandas()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7c1dc0f4",
   "metadata": {},
   "source": [
    "<div>\n",
    "<style scoped>\n",
    "    .dataframe tbody tr th:only-of-type {\n",
    "        vertical-align: middle;\n",
    "    }\n",
    "\n",
    "    .dataframe tbody tr th {\n",
    "        vertical-align: top;\n",
    "    }\n",
    "\n",
    "    .dataframe thead th {\n",
    "        text-align: right;\n",
    "    }\n",
    "</style>\n",
    "<table border=\"1\" class=\"dataframe\">\n",
    "  <thead>\n",
    "    <tr style=\"text-align: right;\">\n",
    "      <th></th>\n",
    "      <th>question</th>\n",
    "      <th>answer</th>\n",
    "      <th>ground_truth</th>\n",
    "      <th>contexts</th>\n",
    "      <th>answer_correctness</th>\n",
    "      <th>context_recall</th>\n",
    "      <th>context_precision</th>\n",
    "    </tr>\n",
    "  </thead>\n",
    "  <tbody>\n",
    "    <tr>\n",
    "      <th>0</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>根据提供的信息，没有提到张伟所在的部门。如果您能提供更多关于张伟的信息，我可能能够帮助您找到答案。</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>[提供⾏政管理与协调⽀持，优化⾏政⼯作流程。 , 绩效管理部 韩杉 李⻜ I902 041 ...</td>\n",
    "      <td>0.175227</td>\n",
    "      <td>0.0</td>\n",
    "      <td>0.0</td>\n",
    "    </tr>\n",
    "    <tr>\n",
    "      <th>1</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>张伟是人事部门的</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>[李凯 教研部主任 , 牛顿发现了万有引力]</td>\n",
    "      <td>0.193980</td>\n",
    "      <td>0.0</td>\n",
    "      <td>0.0</td>\n",
    "    </tr>\n",
    "    <tr>\n",
    "      <th>2</th>\n",
    "      <td>张伟是哪个部门的？</td>\n",
    "      <td>张伟是教研部的</td>\n",
    "      <td>张伟是教研部的成员</td>\n",
    "      <td>[牛顿发现了万有引力, 张伟 教研部工程师，他最近在负责课程研发]</td>\n",
    "      <td>0.994619</td>\n",
    "      <td>1.0</td>\n",
    "      <td>0.5</td>\n",
    "    </tr>\n",
    "  </tbody>\n",
    "</table>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b35c832",
   "metadata": {},
   "source": [
    "### 更多评价指标\n",
    "除了RAG外，还有许多种大模型或者自然语言处理（NLP）的应用或任务，如Agent、NL2SQL、机器翻译、文本摘要等。Ragas提供了许多可以评测这些任务的指标。\n",
    "\n",
    "\n",
    "<!-- | 评价指标            | 使用场景 | 指标含义                                                                 |\n",
    "|---------------------|----------|--------------------------------------------------------------------------|\n",
    "| [ToolCallAccuracy](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/agents/#example)    | Agent    | 评估 LLM 在识别和调用完成特定任务所需工具方面的表现，该参数由参考工具调用与大模型做出的工具调用比较得到，取值范围是0-1。 |\n",
    "| [DataCompyScore](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/sql/)      | NL2SQL   | 评估大模型生成的SQL语句在数据库检索后得到的结果与正确结果的差异性，取值为0-1。                     |\n",
    "| [LLMSQLEquivalence](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/sql/#non-execution-based-metrics)   | NL2SQL   | 相比于上者，无需真正在数据库中进行检索，只评估大模型生成的SQL语句与正确的SQL语句的区别，取值为0-1。   |\n",
    "| [BleuScore](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/traditional/#bleu-score) | 通用   ｜ 基于 n-gram 评估响应与正确答案之间的相似性。最初被设计用于评估机器翻译系统，评测时无需使用大模型。取值为0-1。微调章节会比较微调前后的 BleuScore 的变化。  ｜ -->\n",
    "\n",
    "| 评价指标            | 使用场景 | 指标含义                                                                 |\n",
    "|---------------------|----------|--------------------------------------------------------------------------|\n",
    "| [ToolCallAccuracy](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/agents/#example)    | Agent    | 评估 LLM 在识别和调用完成特定任务所需工具方面的表现，该参数由参考工具调用与大模型做出的工具调用比较得到，取值范围是0-1。 |\n",
    "| [DataCompyScore](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/sql/)      | NL2SQL   | 评估大模型生成的SQL语句在数据库检索后得到的结果与正确结果的差异性，取值为0-1。                     |\n",
    "| [LLMSQLEquivalence](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/sql/#non-execution-based-metrics)   | NL2SQL   | 相比于上者，无需真正在数据库中进行检索，只评估大模型生成的SQL语句与正确的SQL语句的区别，取值为0-1。   |\n",
    "| [BleuScore](https://docs.ragas.io/en/latest/concepts/metrics/available_metrics/traditional/#bleu-score)           | 通用     | 基于 n-gram 评估响应与正确答案之间的相似性。最初被设计用于评估机器翻译系统，评测时无需使用大模型，取值为0-1。在[2.7教程](2_7_通过微调提升模型的准确度与效率.ipynb)中，你会学习到如何对大模型进行微调，BleuScore就可以用来评测微调带来的收益。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d0ed74ea",
   "metadata": {},
   "source": [
    "## 🔥 课后小测验\n",
    "\n",
    "### 🔍 单选题\n",
    "<details>\n",
    "<summary style=\"cursor: pointer; padding: 12px;  border: 1px solid #dee2e6; border-radius: 6px;\">\n",
    "<b>Context Precision 指标检测的是❓</b>\n",
    "\n",
    "- A. 整体回答质量的评估\n",
    "- B. 评估与提问相关的召回文本段是否排名靠前\n",
    "- C. 生成的答案与召回文本段是否相关\n",
    "- D. 生成的答案是否与提问相关\n",
    "\n",
    "**【点击查看答案】**\n",
    "</summary>\n",
    "\n",
    "<div style=\"margin-top: 10px; padding: 15px; border: 1px solid #dee2e6; border-radius: 0 0 6px 6px;\">\n",
    "\n",
    "✅ **参考答案：B**  \n",
    "📝 **解析**：\n",
    "- Context Precision 衡量检索结果的排序质量，而非内容本身的相关性或最终答案质量。\n",
    "\n",
    "\n",
    "</div>\n",
    "</details>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "447d3e8f",
   "metadata": {},
   "source": [
    "## ✉️ 评价反馈\n",
    "感谢你学习阿里云大模型ACP认证课程，如果你觉得课程有哪里写得好、哪里写得不好，期待你[通过问卷提交评价和反馈](https://survey.aliyun.com/apps/zhiliao/Mo5O9vuie)。\n",
    "\n",
    "你的批评和鼓励都是我们前进的动力。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "learnacp",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
